package cn.zyjblogs.sms.task.service.impl;

import cn.zyjblogs.sms.exception.SmsRuntimeException;
import cn.zyjblogs.sms.provider.SmsSendHandler;
import cn.zyjblogs.sms.task.constant.SendStatus;
import cn.zyjblogs.sms.task.constant.SourceType;
import cn.zyjblogs.sms.task.constant.TaskStatus;
import cn.zyjblogs.sms.task.dto.SmsTaskDTO;
import cn.zyjblogs.sms.task.mapper.SmsTaskMapper;
import cn.zyjblogs.sms.task.po.SmsSendStatusPO;
import cn.zyjblogs.sms.task.po.SmsTaskPO;
import cn.zyjblogs.sms.task.po.SmsTemplatePO;
import cn.zyjblogs.sms.task.service.SmsSendStatusService;
import cn.zyjblogs.sms.task.service.SmsTaskService;
import cn.zyjblogs.sms.task.service.SmsTemplateService;
import cn.zyjblogs.sms.task.vo.SmsTaskVO;
import cn.zyjblogs.starter.common.entity.context.BaseContext;
import cn.zyjblogs.starter.common.entity.dto.Kv;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
import cn.zyjblogs.starter.common.utils.bean.BeanUtils;
import cn.zyjblogs.starter.common.utils.collections.CollectionUtils;
import cn.zyjblogs.starter.common.utils.lang.Asserts;
import cn.zyjblogs.starter.common.utils.string.StringUtils;
import cn.zyjblogs.starter.mybatisplus.utils.IdUtils;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zhuyijun
 */

@Service
@RequiredArgsConstructor
public class SmsTaskServiceImpl extends ServiceImpl<SmsTaskMapper, SmsTaskPO> implements SmsTaskService {

    private final SmsTemplateService smsTemplateService;
    private final SmsSendStatusService smsSendStatusService;
    private final TransactionTemplate transactionTemplate;
    private final SmsSendHandler smsSendHandler;
    private static final Logger log = LoggerFactory.getLogger(SmsTaskServiceImpl.class);

    @Override
    public SmsTaskVO sendSmsTask(SmsTaskDTO data) {
        if (data.getSourceType() == null) {
            data.setSourceType(SourceType.OTHER);
        }
        SmsTemplatePO template = smsTemplateService.getById(data.getTemplateId());
        //数据校验
        dataValid(data, template);
        SmsTaskPO smsTaskPO = transactionTemplate.execute(SmsTask -> {
            String tenantId = BaseContext.getTenantId();
            String userId = BaseContext.getUserId();
            LocalDateTime now = LocalDateTime.now();
            SmsTaskPO smsTask = BeanUtils.map(data, SmsTaskPO.class);
            smsTask.setId(IdUtils.nextUUID());
            smsTask.setStatus(TaskStatus.WAITING);
            smsTask.setTemplateParams(JSON.toJSONString(data.getTemplateParams()));
            smsTask.setTenantId(tenantId);
            smsTask.setCreateUserId(userId);
            smsTask.setCreateTime(now);
            this.save(smsTask);
            List<SmsSendStatusPO> list = new LinkedList<>();
            Set<String> telNums = new HashSet<>(data.getTelNums());
            telNums.forEach(tel -> list.add(SmsSendStatusPO.builder()
                    .taskId(smsTask.getId())
                    .submitStatus(SendStatus.WAITING)
                    .telNum(tel)
                    .createTime(now)
                    .createUserId(userId)
                    .tenantId(tenantId)
                    .build()));
            if (!CollectionUtils.isEmpty(list)) {
                smsSendStatusService.saveBatch(list, 1000);
            }
            return smsTask;
        });
        if (smsTaskPO == null) {
            throw new SmsRuntimeException(HttpCode.BAD_REQUEST, "短信任务保存失败");
        }
        return taskPreHandle(smsTaskPO, template);
    }

    public SmsTaskVO taskPreHandle(SmsTaskPO data, SmsTemplatePO template) {
        if (Boolean.TRUE.equals(data.getDraft())) {
            return BeanUtils.map(data, SmsTaskVO.class);
        }
        //发送时间不为空时处理定时任务
        if (data.getSendTime() != null) {
            scheduledTasks(data);
            return BeanUtils.map(data, SmsTaskVO.class);
        }
        smsSendHandler.send(data, template);
        return BeanUtils.map(data, SmsTaskVO.class);
    }


    /**
     * 定时任务处理逻辑
     *
     * @param data
     * @return void
     * @author zhuyijun
     * @date 2023/4/11 18:06
     */
    private void scheduledTasks(SmsTaskPO data) {
        //TODO 定时任务
        smsSendHandler.send(data.getId());
    }

    public void dataValid(SmsTaskDTO data, SmsTemplatePO template) {
        Asserts.notNull(template, "查询不到该模板，请选择正确的短信模板");
        Asserts.notNull(template.getTemplateCode(), "模版编号不能为空");
        Asserts.notNull(template.getContent(), "模版内容不能为空");
        Asserts.notEmpty(data.getTelNums(), "请填写短信接收人");
        // 验证定时发送的时间，至少大于（当前时间+5分钟） ，是为了防止 定时调度或者是保存数据跟不上
        if (data.getSendTime() != null) {
            boolean flag = LocalDateTime.now().plusMinutes(4).isBefore(data.getSendTime());
            Asserts.isTrue(flag, "定时发送时间至少在当前时间的5分钟之后,当前填写时间: {}", data.getSendTime());
        }
        if (StringUtils.isEmpty(data.getContent())) {
            data.setContent(processTemplate(template.getContent(), template.getProviderType().getRegex(), data.getTemplateParams()));
        } else if (data.getContent().length() > 100) {
            throw new SmsRuntimeException(HttpCode.BASE_PARAMS_VALID_FAIL, "发送内容不能超过500字");
        }
    }


    private static String processTemplate(String template, String regex, List<Kv<String, String>> params) {
        log.info("regex={}, template={}, params={}", regex, template, params);
        if (StringUtils.isEmpty(template)) {
            return StringUtils.EMPTY;
        }
        if (CollectionUtils.isEmpty(params)) {
            params = new LinkedList<>();
        }
        StringBuilder sb = new StringBuilder();
        Matcher m = Pattern.compile(regex).matcher(template);
        int index = 0;
        while (m.find()) {
            String value = "";
            if (index + 1 <= params.size()) {
                Kv<String, String> kv = params.get(index);
                value = kv.getV();
            }
            value = value == null ? "" : value;
            if (value.contains("$")) {
                value = value.replace("\\$", "\\\\\\$");
            }
            m.appendReplacement(sb, value);
            index++;
        }
        m.appendTail(sb);
        if (index < params.size()) {
            throw new SmsRuntimeException(StringUtils.format("模板中识别出的参数个数: {} 少于传入的参数个数: {}", index, params.size()));
        } else if (index > params.size()) {
            throw new SmsRuntimeException(StringUtils.format("模板中识别出的参数个数: {} 多于传入的参数个数: {}", index, params.size()));
        }
        return sb.toString();
    }
}
